package com.ethank.utils;


import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequests;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.UrlEncodedFormEntity;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.InputStreamEntity;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.hc.core5.net.URIBuilder;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.ssl.TrustStrategy;
import org.apache.hc.core5.util.Timeout;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HttpClientUtil {
    /**
     * 日志.
     */
    private static Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    public static CloseableHttpClient httpClient = HttpClients.createDefault();

    static {
        logger.info("设置httpclient5超时时间,tls,连接池等属性");
        // 设置超时时间
        RequestConfig config =
                RequestConfig.custom().setConnectTimeout(Timeout.ofMilliseconds(60000L)).setConnectionRequestTimeout(Timeout.ofMilliseconds(60000L)).setResponseTimeout(Timeout.ofMilliseconds(60000L)).build();
        // 未设置支持ssl
//		httpClient = HttpClients.custom().setDefaultRequestConfig(config).build();
        // httpclient5经测试不设置支持ssl，也能调用https接口
        try {
            httpClient =
                    HttpClients.custom().setDefaultRequestConfig(config).setConnectionManager(getHttpClientConnectionManager()).build();
        } catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) {
            httpClient = HttpClients.createDefault();
            e.printStackTrace();
        }

    }

    private static HttpClientConnectionManager getHttpClientConnectionManager() throws NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {
        // 设置连接池最大连接数1000,最大并发数200，及支持ssl,tls
        return PoolingHttpClientConnectionManagerBuilder.create().setMaxConnTotal(1000).setMaxConnPerRoute(200).setSSLSocketFactory(getSslConnectionSocketFactory()).build();
    }

    /**
     * 支持SSL
     *
     * @return SSLConnectionSocketFactory
     */
    private static SSLConnectionSocketFactory getSslConnectionSocketFactory() throws NoSuchAlgorithmException,
            KeyStoreException, KeyManagementException {
        TrustStrategy acceptingTrustStrategy = (x509Certificates, s) -> true;
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        return new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
        // 链接tls版本协议，不指定也可以
//		return new SSLConnectionSocketFactory(sslContext,  new String[] {"TLSv1.1","TLSv1.2","TLSv1.3"},
//				  null, new NoopHostnameVerifier());

    }

    /**
     * get方式请求
     *
     * @param url
     * @return
     */
    public static String get(String url) {
        return get(url, null, null);
    }

    /**
     * @param url
     * @param params
     * @return
     */
    public static String get(String url, Map<String, Object> headers, Map<String, Object> params) {
        logger.info("httpclient5 get start url=" + url + "headers=" + headers + ",params=" + params);
        String resultContent = null;
        HttpGet httpGet = new HttpGet(url);
        // 设置header
        if (headers != null) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                httpGet.addHeader(entry.getKey(), entry.getValue());
            }
        }
        if (params != null && params.size() > 0) {
            // 表单参数
            List<NameValuePair> nvps = new ArrayList<>();
            // GET 请求参数，如果中文出现乱码需要加上URLEncoder.encode
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
            }
            // 增加到请求 URL 中
            try {
                URI uri = new URIBuilder(new URI(url)).addParameters(nvps).build();
                httpGet.setUri(uri);
            } catch (URISyntaxException e) {
                throw new RuntimeException(e);
            }
        }
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            // 获取状态码
            HttpEntity entity = response.getEntity();
            // 获取响应信息
            resultContent = EntityUtils.toString(entity, "UTF-8");
            logger.info("httpclient5 get end url=" + url + "headers=" + headers + ",params=" + params + ",result=" + resultContent);
            // 确保流被完全消费
            EntityUtils.consume(entity);
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("Exception httpclient5 get url=" + url + "headers=" + headers + ",params=" + params + "," +
                    "exception=" + e.getStackTrace());

        }

        return resultContent;
    }

    /**
     * post form请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String postForm(String url, Map<String, String> headers, Map<String, Object> params) {
        logger.info("httpclient5 postForm start url=" + url + "headers=" + headers + ",params=" + params);

        String result = null;
        HttpPost httpPost = new HttpPost(url);
        // 设置header
        if (headers != null) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                httpPost.addHeader(entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
        httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        // 表单参数
        List<NameValuePair> nvps = new ArrayList<>();
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
        }
        // 注意编码为UTF-8,否则中文会出现乱码
        httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            HttpEntity entity = response.getEntity();
            // 获取响应信息
            result = EntityUtils.toString(entity, "UTF-8");
            logger.info("httpclient5 postForm end url=" + url + "headers=" + headers + ",params=" + params + ",result"
                    + "=" + result);

            // 确保流被完全消费
            EntityUtils.consume(entity);
        } catch (Exception e) {
            e.printStackTrace();
            logger.info("Exception httpclient5 postForm url=" + url + "headers=" + headers + ",params=" + params + ","
                    + "exception=" + e.getStackTrace());

        }

        return result;
    }

    /**
     * post form请求,返回Response，有些请求需要拿到header
     *
     * @param url
     * @param params
     * @return
     */
    public static CloseableHttpResponse postFormReturnResponse(String url, Map<String, Object> headers, Map<String,
            Object> params) {
        CloseableHttpResponse response = null;
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");

        // 设置header
        if (headers != null) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                httpPost.addHeader(entry.getKey(), entry.getValue());
            }
        }
        // 表单参数
        List<NameValuePair> nvps = new ArrayList<>();
        // POST 请求参数
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
        }
        httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));

        try {
            response = httpClient.execute(httpPost);
//			Header[] header=response.getHeaders();
//			HttpEntity entity = response.getEntity();
//			// 获取响应信息
//			result = EntityUtils.toString(entity);
//			// 确保流被完全消费
//			EntityUtils.consume(entity);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return response;
    }

    /**
     * post json请求
     *
     * @param url
     * @param jsonBody
     * @return
     */
    public static String postJson(String url, String jsonBody) {
        logger.info("httpclient5 postJson start url=" + url + ",jsonBody=" + jsonBody);
        String result = null;
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Content-Type", "application/json;charset=UTF-8");
        httpPost.setEntity(new StringEntity(jsonBody, ContentType.APPLICATION_JSON));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            // 获取响应信息
            result = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (IOException | ParseException e) {
            e.printStackTrace();
            logger.info("Exception postJson postForm url=" + url + ",jsonBody=" + jsonBody + ",exception=" + e.getStackTrace());

        }
        logger.info("httpclient5 postJson end url=" + url + ",result=" + result);

        return result;
    }

    /**
     * post stream请求(file,json,xml转stream)
     *
     * @param url
     * @param jsonBody
     * @return
     */
    public static String postStream(String url, String params, ContentType contentType) {
        String result = null;
        final HttpPost httppost = new HttpPost(url);
        InputStream inputStream = new ByteArrayInputStream(new String(params).getBytes());
        final InputStreamEntity reqEntity = new InputStreamEntity(inputStream, -1, contentType);
        // 也可以使用 FileEntity 的形式
        // FileEntity reqEntity = new FileEntity(new File(params),
        // ContentType.APPLICATION_JSON);

        httppost.setEntity(reqEntity);
        try (final CloseableHttpResponse response = httpClient.execute(httppost)) {
            System.out.println("----------------------------------------");
            System.out.println(response.getCode() + " " + response.getReasonPhrase());
            result = EntityUtils.toString(response.getEntity(), "UTF-8");
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * getAsync异步请求，回调获取结果
     *
     * @param url
     * @return
     */
    public static String getAsync(String url) {
        try (CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault()) {
            httpclient.start();
            SimpleHttpRequest request = SimpleHttpRequests.get(url);
            httpclient.execute(request, new FutureCallback<SimpleHttpResponse>() {
                @Override
                public void completed(SimpleHttpResponse response2) {
                    System.out.println("getAsync:" + request.getRequestUri() + "->" + response2.getCode());
                }

                @Override
                public void failed(Exception ex) {

                    System.out.println("getAsync:" + request.getRequestUri() + "->" + ex);
                }

                @Override
                public void cancelled() {

                    System.out.println("getAsync:" + request.getRequestUri() + " cancelled");
                }

            });

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }


}